﻿using Marmat.Forms.Win32API;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace Marmat.Forms.Skin
{
    /// <summary>
    /// 带阴影的无边框窗口
    /// </summary>
    public partial class NoneBorderForm : System.Windows.Forms.Form
    {

        private bool m_aeroEnabled;// variables for box shadow
        private Dictionary<FormButton, Rectangle> buttonRectangle = new Dictionary<FormButton, Rectangle>();
        /// <summary>
        /// 窗口边框的画笔
        /// </summary>
        private readonly Pen _borderPen = new Pen(Color.FromArgb(54, 108, 179));

        /// <summary>
        /// 构建 带阴影的无边框窗口 实例。
        /// </summary>
        public NoneBorderForm()
        {
            InitializeComponent();
            this.MinimumButton.Normal = Properties.Resources.form_min_button;
            this.MaximumButton.Normal = Properties.Resources.form_max_button;
            this.RestoreButton.Normal = Properties.Resources.form_restore_normal_button;
            this.CloseButton.Normal = Properties.Resources.form_close_button;
            this.CloseButton.Hover = Properties.Resources.form_close_foucs_button;
            this.Padding = new Padding(0);
            if (!this.DesignMode)
                this.MaximizedBounds = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea;
        }

        protected override CreateParams CreateParams
        {
            get
            {
                m_aeroEnabled = CheckAeroEnabled();
                CreateParams cp = base.CreateParams;
                if (!m_aeroEnabled)
                    cp.ClassStyle |= Messages.CS_DROPSHADOW;
                return cp;
            }
        }

        [DllImport("user32.dll")]//拖动无窗体的控件
        public static extern bool ReleaseCapture();
        [DllImport("user32.dll")]
        public static extern bool SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);
        public const int WM_SYSCOMMAND = 0x0112;
        public const int SC_MOVE = 0xF010;
        public const int HTCAPTION = 0x0002;
        const int WM_NCHITTEST = 0x0084;
        const int HTLEFT = 10;
        const int HTRIGHT = 11;
        const int HTTOP = 12;
        const int HTTOPLEFT = 13;
        const int HTTOPRIGHT = 14;
        const int HTBOTTOM = 15;
        const int HTBOTTOMLEFT = 0x10;
        const int HTBOTTOMRIGHT = 17;

        /// <summary>
        /// 获取或设置控件内的空白。
        /// </summary>
        /// <returns>表示控件的内部间距特征的 System.Windows.Forms.Padding。</returns>
        [DefaultValue(typeof(Padding), "0")]
        public new Padding Padding
        {
            get => padding;
            set
            {
                padding = value;
                base.Padding = new Padding(padding.Left, padding.Top + 26, padding.Right, padding.Bottom);
            }
        }

        /// <summary>
        /// 获取或设置一个值，该值指示在该窗体的标题栏中是否显示控件框。
        /// </summary>
        /// <returns>true 如果窗体的窗体; 右上角显示控件框否则为false。 默认值为 true。</returns>
        public new bool ControlBox
        {
            get => base.ControlBox;
            set
            {
                base.ControlBox = value;
                this.Padding = padding;
            }
        }

        /// <summary>
        /// 设置或返回关闭按钮的皮肤图片。
        /// </summary>
        [Category("Skin"), Description("设置或返回关闭按钮的皮肤图片。")]
        [Browsable(false), Localizable(true), EditorBrowsable(EditorBrowsableState.Always)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), TypeConverter(typeof(FormConverter))]
        public FormButton CloseButton
        {
            get
            {
                if (_CloseButtonImage is null)
                {
                    _CloseButtonImage = new FormButton();
                    _CloseButtonImage.ToolTip = "关闭";
                }
                return _CloseButtonImage;
            }
        }
        FormButton _CloseButtonImage;

        /// <summary>
        /// 设置或返回最大化按钮的皮肤图片。
        /// </summary>
        [Category("Skin"), Description("设置或返回最大化按钮的皮肤图片。")]
        [Browsable(false), Localizable(true), EditorBrowsable(EditorBrowsableState.Always)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), TypeConverter(typeof(FormConverter))]
        public FormButton MaximumButton
        {
            get
            {
                if (_MaximumButtonImage is null)
                {
                    _MaximumButtonImage = new FormButton();
                    _MaximumButtonImage.ToolTip = "最大化";
                }
                return _MaximumButtonImage;
            }
        }
        FormButton _MaximumButtonImage;

        /// <summary>
        /// 设置或返回恢复默认大小按钮的皮肤图片。
        /// </summary>
        [Category("Skin"), Description("设置或返回恢复默认大小按钮的皮肤图片。")]
        [Browsable(false), Localizable(true), EditorBrowsable(EditorBrowsableState.Always)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), TypeConverter(typeof(FormConverter))]
        public FormButton RestoreButton
        {
            get
            {
                if (_RestoreButtonImage is null)
                {
                    _RestoreButtonImage = new FormButton();
                    _RestoreButtonImage.ToolTip = "还原";
                }
                return _RestoreButtonImage;
            }
        }
        FormButton _RestoreButtonImage;

        /// <summary>
        /// 设置或返回最小化按钮的皮肤图片。
        /// </summary>
        [Category("Skin"), Description("设置或返回最小化按钮的皮肤图片。")]
        [Browsable(false), Localizable(true), EditorBrowsable(EditorBrowsableState.Always)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), TypeConverter(typeof(FormConverter))]
        public FormButton MinimumButton
        {
            get
            {
                if (_MinimumButtonImage is null)
                {
                    _MinimumButtonImage = new FormButton();
                    _MinimumButtonImage.ToolTip = "最小化";
                }
                return _MinimumButtonImage;
            }
        }
        FormButton _MinimumButtonImage;
        Point mouse_move_location;
        FormButton mouse_formButton;
        private Padding padding;

        private List<FormButton> ButtonList
        {
            get
            {
                List<FormButton> Items = new();
                if (this.ControlBox)
                {
                    Items.Add(CloseButton);
                    if (this.MaximizeBox && this.WindowState != FormWindowState.Maximized)
                        Items.Add(this.MaximumButton);
                    if (this.MaximizeBox && this.WindowState == FormWindowState.Maximized)
                        Items.Add(this.RestoreButton);
                    if (this.MinimizeBox)
                        Items.Add(this.MinimumButton);
                }
                //if (this.HelpButton)
                //    Items.Add(this.HelpButtonImage);
                //foreach (FormButton Item in this.FormButtonCollection)
                //{
                //    if (Item.Visible)
                //        Items.Add(Item);
                //}                
                return Items;
            }
        }


        private bool CheckAeroEnabled()
        {
            if (Environment.OSVersion.Version.Major >= 6)
            {
                int enabled = 0;
                Win32API.DwmApi.DwmIsCompositionEnabled(ref enabled);
                return (enabled == 1);
            }
            return false;
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            buttonRectangle.Clear();
            if (this.ControlBox)
            {
                Size size = new(36, 26);
                var buttonList = this.ButtonList;
                buttonList.Reverse();
                int left = this.Width - size.Width * buttonList.Count;
                foreach (var button in buttonList)
                {
                    Image img = button.Normal;
                    Rectangle rectangle = new(left, 0, size.Width, size.Height);
                    if (button == this.mouse_formButton)
                    {
                        if (button == this.CloseButton)
                            e.Graphics.FillRectangle(Brushes.Red, rectangle);
                        else
                            e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(229, 229, 229)), rectangle);
                        img = button.Hover ?? img;
                    }
                    buttonRectangle[button] = rectangle;
                    e.Graphics.DrawImage(img, left + (size.Width - img.Width) / 2, 5);
                    left += size.Width;
                }
            }

            if (!m_aeroEnabled) //如果没有启用玻璃特效，则绘制一个边框效果，好区分窗体的区域
                e.Graphics.DrawRectangle(this._borderPen, new Rectangle(0, 0, this.Width - 1, this.Height - 1));
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            this.mouse_move_location = e.Location;
            var button = this.buttonRectangle.FirstOrDefault(x => x.Value.Contains(e.Location)).Key;
            if (!object.Equals(button, this.mouse_formButton))
            {
                this.mouse_formButton = button;
                this.Invalidate();
            }
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            if (this.mouse_formButton is not null)
            {
                this.mouse_formButton = null;
                this.Invalidate();
            }
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            if (this.ControlBox && this.mouse_formButton is null && e.Location.Y < base.Padding.Top)
            {
                //拖动窗体
                ReleaseCapture();
                SendMessage(this.Handle, WM_SYSCOMMAND, SC_MOVE + HTCAPTION, 0);
            }
        }

        protected override void OnSizeChanged(EventArgs e)
        {
            base.OnSizeChanged(e);
            this.Invalidate();
        }

        protected override void OnMouseClick(MouseEventArgs e)
        {
            base.OnMouseClick(e);
            var button = this.buttonRectangle.FirstOrDefault(x => x.Value.Contains(e.Location)).Key;
            if (button == this.CloseButton)
                this.Close();
            else if (button == this.MinimumButton)
                this.WindowState = FormWindowState.Minimized;
            else if (button == this.RestoreButton)
                this.WindowState = FormWindowState.Normal;
            else if (button == this.MaximumButton)
                this.WindowState = FormWindowState.Maximized;
        }

        protected override void WndProc(ref Message m)
        {
            base.WndProc(ref m);
            if (!this.DesignMode)
            {
                switch (m.Msg)
                {
                    //处理无边框窗体的调整大小
                    case WM_NCHITTEST:
                        if (this.SizeGripStyle == SizeGripStyle.Hide || this.AutoSizeMode == AutoSizeMode.GrowAndShrink)
                            return;
                        Point vPoint = new((int)m.LParam & 0xFFFF,
                            (int)m.LParam >> 16 & 0xFFFF);
                        vPoint = PointToClient(vPoint);
                        if (vPoint.X <= 5)
                            if (vPoint.Y <= 5)
                                m.Result = (IntPtr)HTTOPLEFT;
                            else if (vPoint.Y >= ClientSize.Height - 5)
                                m.Result = (IntPtr)HTBOTTOMLEFT;
                            else m.Result = (IntPtr)HTLEFT;
                        else if (vPoint.X >= ClientSize.Width - 5)
                            if (vPoint.Y <= 5)
                                m.Result = (IntPtr)HTTOPRIGHT;
                            else if (vPoint.Y >= ClientSize.Height - 5)
                                m.Result = (IntPtr)HTBOTTOMRIGHT;
                            else m.Result = (IntPtr)HTRIGHT;
                        else if (vPoint.Y <= 5)
                            m.Result = (IntPtr)HTTOP;
                        else if (vPoint.Y >= ClientSize.Height - 5)
                            m.Result = (IntPtr)HTBOTTOM;
                        break;
                    // 处理无边框中的阴影效果
                    case MessageEnum.WM_NCPAINT:
                        if (m_aeroEnabled)
                        {
                            var v = 2;
                            Win32API.DwmApi.DwmSetWindowAttribute(this.Handle, 2, ref v, 4);
                            Win32API.MARGINS margins = new()
                            {
                                bottomHeight = 1,
                                leftWidth = 1,
                                rightWidth = 1,
                                topHeight = 1
                            };
                            Win32API.DwmApi.DwmExtendFrameIntoClientArea(this.Handle, ref margins);
                        }
                        break;
                    default:
                        break;
                }
            }
        }
    }
}
